

/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield 
 *
 * This library is open source and may be redistributed and/or modified under  
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or 
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 * OpenSceneGraph Public License for more details.
*/
/*
*	Customized OSG Viewer
*	Written by Andrew Moore
*	Arctic Region Supercomputing Center (ARSC)
*	Created June 18, 2009
*	Last Revised September 02, 2009
*
*
*	This program is a customized viewer to be used in combination with the HUD.  It has all the features of the standard osgViewer, 
*	with the additional functionality of displaying the HUD.  
*
*	Changes:
*		9/01/2009:
*			-Added in the key mapping for scrolling through descriptive text, currently mapped to the 'b' key.
*		9/02/2009:
*			-Updated the program header to correct formatting errors. 
*/

#include "HUD.h"

int main( int argc, char **argv)
{

	// Variable Declarations

	std::string fileName = "";			// Will be used to get the location of the HUD text file.
	std::string temp = "";				// Will hold the unformatted string with the location of the file.
	std::string colorStr;				// Will hold the values for the clear-color commandline switch
	osg::ref_ptr<osg::Group> HUD = new osg::Group;	//Group that will hold the HUD
	
	// Use an ArgumentParser object to manage the program arguments
	
	osg::ArgumentParser arguments(&argc,argv); 

	arguments.getApplicationUsage()->setApplicationName(arguments.getApplicationName());	    
	arguments.osg::ArgumentParser::getApplicationUsage()->addCommandLineOption("-h or --help","List command line options.");
	arguments.osg::ArgumentParser::getApplicationUsage()->addCommandLineOption("--pbuffer","Create a 512x512 pixel buffer.");
	arguments.osg::ArgumentParser::getApplicationUsage()->addCommandLineOption("--pbuffer <width> <height>","Create a pixel buffer of specified dimensions.");
	arguments.osg::ArgumentParser::getApplicationUsage()->addCommandLineOption("--window","Create a 512x512 graphics window.");
	arguments.osg::ArgumentParser::getApplicationUsage()->addCommandLineOption("--window <width> <height>","Create a graphics window of specified dimensions.");
	arguments.osg::ArgumentParser::getApplicationUsage()->addCommandLineOption("--delay <micoseconds>","Set a delay in microseconds before all OpenGL object operations.");
	arguments.osg::ArgumentParser::getApplicationUsage()->addCommandLineOption("--texture <width> <height> <depth>","Allocate a 3D texture of specified dimensions.");
	arguments.osg::ArgumentParser::getApplicationUsage()->addCommandLineOption("--texture <width> <height>","Allocate a 2D texture of specified dimensions.");
	arguments.osg::ArgumentParser::getApplicationUsage()->addCommandLineOption("--texture <width>","Allocate a 1D texture of specified dimensions.");
	arguments.osg::ArgumentParser::getApplicationUsage()->addCommandLineOption("--geometry <width> <height>","Allocate a osg::Geometry representing a grid of specified size, using OpenGL Dislay Lists.");
	arguments.osg::ArgumentParser::getApplicationUsage()->addCommandLineOption("--geometry-va <width> <height>","Allocate a osg::Geometry representing a grid of specified size, using Vertex Arrays.");
	arguments.osg::ArgumentParser::getApplicationUsage()->addCommandLineOption("--geometry-vbo <width> <height>","Allocate a osg::Geometry representing a grid of specified size, using Vertex Buffer Objects.");
	arguments.osg::ArgumentParser::getApplicationUsage()->addCommandLineOption("--fbo <width> <height>","Allocate a FrameBufferObject of specified dimensions.");
	arguments.osg::ArgumentParser::getApplicationUsage()->addCommandLineOption("-c <num>","Set the number of contexts to create of each type specified.");
	arguments.osg::ArgumentParser::getApplicationUsage()->addCommandLineOption("-g <num>","Set the number of GL objects to create of each type specified.");
	arguments.osg::ArgumentParser::getApplicationUsage()->addCommandLineOption("--stereo <option>","Set the viewer to use stereo");
	arguments.osg::ArgumentParser::getApplicationUsage()->addCommandLineOption("--clear-color <color>","Set the background color");
	arguments.osg::ArgumentParser::getApplicationUsage()->addCommandLineOption("--hudFile <string>","Set the location of the HUD file.");
	osg::DisplaySettings::instance()->readCommandLine(arguments);
   	osgDB::readCommandLine(arguments);


	//osg::notify(osg::DEBUG) << "Arguments Parsed" << std::endl;

	
	// Read the scene from the list of file specified commandline args.

	osg::ref_ptr<osg::Node> scene = osgDB::readNodeFiles(arguments);

	//osg::notify(osg::DEBUG) << "Arguments sent to scene" << std::endl;


	//If not loaded, assume no arguments passed in and try to use default mode instead.

	if (arguments.argc() <= 1)
	{
		arguments.osg::ArgumentParser::getApplicationUsage() -> write(std::cout,osg::ApplicationUsage::COMMAND_LINE_OPTION);
		return 1;
	}

	if (!scene)
	{
		osg::notify(osg::NOTICE) << "No model loaded" << std::endl;
		return 1;
	}	

	// Get the name and location of the text file that the HUD will be using.

	while(arguments.read("--hudFile",temp))
	{
		for (int i = 0; i < temp.length(); i++) // Convert the string into a usable form.
		{
			if (temp[i] == '/')
				fileName += "/";
			fileName += temp[i];
		}	
	}

	// Set up the camera manipulators

	osg::ref_ptr<osgGA::KeySwitchMatrixManipulator> keyswitchManipulator = new osgGA::KeySwitchMatrixManipulator;

	keyswitchManipulator -> addMatrixManipulator('1',"Trackball",new osgGA::TrackballManipulator());	// Trackball manipulator
	keyswitchManipulator -> addMatrixManipulator('2',"Flight",new osgGA::FlightManipulator());		// Flight manipulator
	keyswitchManipulator -> addMatrixManipulator('3',"Drive",new osgGA::DriveManipulator());		// Drive manipulator
	keyswitchManipulator -> addMatrixManipulator('4',"Terrain",new osgGA::TerrainManipulator());		// Terrain manipulator
	//keyswitchManipulator -> addMatrixManipulator('5',"Spherical",new osgGA::SphericalManipulator());

	std::string pathfile;
	char keyForAnimationPath = '6';
		
	while (arguments.read("-p",pathfile)) // The user has specified a file in which an animation path is located.
	{
		osgGA::AnimationPathManipulator* apm = new osgGA::AnimationPathManipulator(pathfile); // Set the manipulator to use this path.

		if (apm || !apm -> valid())
		{
			unsigned int num = keyswitchManipulator -> getNumMatrixManipulators();
			keyswitchManipulator -> addMatrixManipulator (keyForAnimationPath,"Path",apm);
			keyswitchManipulator -> selectMatrixManipulator(num);
			++keyForAnimationPath;
		}
	}
	// Construct viewer

	osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;

	//Set up a group to contain the nodes in the scene

	osg::ref_ptr<osg::Group> root = new osg::Group;

	//Create the group containing the main scene

	osg::ref_ptr<osg::Group> mainScene = new osg::Group;

		
	//This line isn't neccasary, the window will be set up by the environmental variables in the tour application scripts.
	//viewer -> setUpViewInWindow(32,32,3840,1024); //Have the viewer set up a 1040W x 768H window
	

	//Make sure the scene is valid, and if so add it to the main scene.
	if (scene.valid()) 
		mainScene -> addChild(scene.get()); 

	// Add the main scene to the root group.
	root -> addChild(mainScene);	

	if (std::strcmp(fileName.c_str(),"") !=0) // Test to make sure the HUD is needed before implementing.
	{		
		// Insert the new HUD object right here.

		HUD -> setNodeMask(0x0);	//Make HUD invisible by default.	

		// Create the HUD and add the HUD subgraph

		HUD -> addChild(createHUD(fileName));  //Attach the HUD geometry to the HUD group
		root -> addChild(HUD);
	}
	// Set scene graph to render

	viewer -> setSceneData(root.get());  //Give the root group to the viewer to render.

	viewer -> setCameraManipulator(keyswitchManipulator.get());	// Set the camera to use the current keyswitch manipulator.

	// Add all of the event handlers

	viewer -> addEventHandler(new HUDEventHandler('a',HUD));  							//Event handler will hide the HUD
	viewer -> addEventHandler(new HUDEventHandler('b',HUD));							// Event handler used to update the dynamic text
	viewer -> addEventHandler(new osgViewer::ThreadingHandler); 							// Thread model handler
	viewer -> addEventHandler(new osgViewer::WindowSizeHandler); 							// Window size toggle handler
	viewer -> addEventHandler(new osgViewer::StatsHandler); 							// Add the stats handler
	viewer -> addEventHandler(new osgViewer::HelpHandler(arguments.getApplicationUsage())); 			// Display help
	viewer -> addEventHandler(new osgViewer::RecordCameraPathHandler); 						// Record the camera's path.
	viewer -> addEventHandler(new osgViewer::LODScaleHandler);							// Change LOD scale
	viewer -> addEventHandler(new osgViewer::ScreenCaptureHandler); 						// Add the screen capture handler.
	viewer -> addEventHandler(new osgGA::StateSetManipulator(viewer -> getCamera() -> getOrCreateStateSet())); 	// Change the state of model

	//Set the background color using the getHeightclear color variables.

	while(arguments.read("--clear-color",colorStr)) //The --clear-color switch has been used, so we'll set the background.
	{
		float r,g,b;		// Will hold the rgb values supplied by the command line.
		float a = 1.0f; 	// Holds the alpha value, as supplied by the command line, defaults at 1.0.
	
		int cnt = sscanf(colorStr.c_str(),"%f,%f,%f,%f",&r,&g,&b,&a);	// Grab the values from the command line.
	
		if ( cnt == 3 || cnt == 4) 	// Set the clear color.
			viewer -> getCamera() -> setClearColor(osg::Vec4(r,g,b,a));
		else	//Color was invalid.
			osg::notify(osg::WARN) << "Invalid clear color \"" << colorStr << "\"" << std::endl;
	}
	
	arguments.reportRemainingOptionsAsUnrecognized();	// Convert any unhandled options to errors and write out later.


	if(arguments.errors())	// Write out error messages associated with arguments, and kill the program with an error status.
	{
		arguments.writeErrorMessages(std::cout);
		return 1;
	}	
    	// Optimize the scene graph, remove redundant nodes and state etc..

	osgUtil::Optimizer optimizer;
		
	optimizer.optimize(root);

	return viewer -> run();  //Enter the main loop 
}
